package org.lvxiao.example.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

public class ReentrantLockDemo {

	public static void main(String[] args) {
		ReentrantLock lock = new ReentrantLock();
		for(int i = 0 ; i <=100; i++) {
			new Thread(()->{
				lock.lock();
				System.out.println(Thread.currentThread().getName() + " lock");
				System.out.println(Thread.currentThread().getName() +" unlock");
				lock.unlock();
			}, "Thread-" + i).start();
		}
	}
	
	static class ReentrantLock implements Lock{
		AtomicInteger state = new AtomicInteger(0);
		
		AtomicReference<Node> tail = new AtomicReference<>();
		
		AtomicReference<Node> header = new AtomicReference<>();

		Thread lockThread = null;
		
		class Node{
			Thread t;
			Node pre;
			Node next;
			
			public Node(Thread t, Node pre, Node next) {
				this.t = t;
				this.pre = pre;
				this.next = next;
			}
		}

		@Override
		public void lock() {
			if(!tryLock()) {
				addQueue();
			}
		}
		
		private void addQueue() {
			Thread currentThread = Thread.currentThread();
			Node headerNode = header.get();
			boolean hasAdd = false;
			Node currentNode = new Node(currentThread, null, null);
			//头为空,第一次插入
			if(headerNode == null && header.compareAndSet(null,currentNode)) {
				tail = header;
				hasAdd = true;
			}
			
			while (!hasAdd) {
				Node tailNode = tail.get();
				if(tailNode == null) {
					continue;
				}
				if(tail.compareAndSet(tailNode, currentNode)) {
					tailNode.next = currentNode;
					currentNode.pre = tailNode;
					hasAdd = true;
				}
			}
			LockSupport.park(this);
		}
		
		@Override
		public void lockInterruptibly() throws InterruptedException {
			
		}

		@Override
		public boolean tryLock() {
			
			if(state.compareAndSet(0, 1)) {
				lockThread = Thread.currentThread();
				return true;
			} else if(lockThread == Thread.currentThread()) {
				state.incrementAndGet();
				return true;
			}
			return false;
		}

		@Override
		public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
			return false;
		}

		@Override
		public void unlock() {
			if(lockThread != Thread.currentThread()) {
				return;
			}
			
			if(state.decrementAndGet() == 0) {
				Node headerNode = null;
				if((headerNode =  header.get()) != null && state.compareAndSet(0, 1)) {
					Node next = headerNode.next;
					header.set(next);
					lockThread = headerNode.t;
					LockSupport.unpark(headerNode.t);
					if(next != null) {
						//gc
						next.pre = null;  
					}
					headerNode = null;
				}
			}
			
		}

		@Override
		public Condition newCondition() {
			return null;
		}
	}
	
}
